/**
 * Copyright (c) 2014-now, secondriver (secondriver@yeah.net).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package secondriver.jfinal.rack.pugin.sql;

import java.io.File;
import java.net.URL;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;

import org.apache.log4j.Logger;

import com.jfinal.kit.StrKit;

/**
 * Sql Configuration Parse.
 * 
 * @author ZhangXiao
 * @time 2014年6月27日
 */
class SqlParse {

	private static Logger logger = Logger.getLogger(SqlParse.class);

	private boolean succeed = false;

	private String sqlFile;

	public SqlParse(String sqlFile) {
		this.sqlFile = sqlFile;
		this.load();
	}

	private void load() {
		if (logger.isInfoEnabled()) {
			logger.info("SqlManagerPlugin loading sql with SqlParse.");
		}
		URL url = Thread.currentThread().getContextClassLoader()
				.getResource("");
		if (null != url) {
			File file = new File(url.getFile(), sqlFile);
			if (!file.exists()) {
				throw new RuntimeException(
						"Please check "
								+ sqlFile
								+ " (default name is 'sql.xml') is not exist in classpath.");
			} else {
				this.parse(file);
			}
		} else {
			throw new RuntimeException("Please check " + sqlFile
					+ " (default name is 'sql.xml') is not exist in classpath.");
		}

	}

	private void parse(File file) {
		JAXBContext context;
		Root root = null;
		try {
			context = JAXBContext.newInstance(Root.class);
			Unmarshaller unmarshaller = context.createUnmarshaller();
			root = (Root) unmarshaller.unmarshal(file);
		} catch (JAXBException e) {
			e.printStackTrace();
			logger.error("Please check " + sqlFile
					+ " (default name is 'sql.xml') format in classpath.\n");
			logger.error("If you don't know how to write sql.xml, your can call SqlKit.sqlXmlInfo() will output introduction information.");
			if (logger.isInfoEnabled()) {
				logger.info("Please check " + sqlFile + " format.");
				logger.info("Sql configuration file (default name is 'sql.xml') format as follow:\n");
				log();
			}
		}
		if (null != root) {
			List<Grounp> grounpList = root.grounpList;
			if (null != grounpList && grounpList.size() != 0) {
				Set<String> grounp = new TreeSet<String>();
				for (Grounp g : grounpList) {
					if (null == g) {
						break;
					}
					if (StrKit.isBlank(g.id) || grounp.contains(g.id)) {
						throw new RuntimeException(
								"Error occurence in the sqlFile '" + sqlFile
										+ "', the <grounp>'s attribute 'id' "
										+ "must be unique and not null/'' .");
					} else {
						grounp.add(g.id);
					}
					List<Sql> sqlList = g.sqlList;
					if (null != sqlList && sqlList.size() != 0) {
						for (Sql s : sqlList) {
							if (null == s) {
								break;
							}
							if (StrKit.isBlank(s.id)) {
								throw new RuntimeException(
										"Error occurrence in the sqlFile '"
												+ sqlFile
												+ "', the <sql>'s attribute 'id' must be unique in their grounp and not null/''.");
							}
							if (SqlKit.isExist(g.id, s.id)) {
								throw new RuntimeException(
										"Error occurence in the sqlFile '"
												+ sqlFile
												+ "', the <sql>'s attribute 'id' must be unique in their <grounp>'s id is "
												+ g.id + ".");
							} else {
								SqlKit.add(g.id, s.id, s.value);
							}
						}
					}
				}
			}
			this.setSucceed(true);
		} else {
			throw new RuntimeException("Please check " + sqlFile
					+ " (default name is 'sql.xml') in classpath.");
		}
	}

	private void log() {
		logger.info("************sql.xml(default)************\n");
		logger.info("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
		logger.info("<jfinal>\n");
		logger.info(" <grounp id='please set grounp id, if not set default is 'default' .'>\n");
		logger.info("    <sql id='Please set sql id, if not set will cause error.'> your sql sentence. </sql>\n");
		logger.info("    ...more sql...\n");
		logger.info(" </grounp>\n");
		logger.info("  ...more grounp...\n");
		logger.info("</jfinl>\n");
		logger.info("************sql.xml(default)************\n");
	}

	private void setSucceed(boolean succeed) {
		this.succeed = succeed;
	}

	public boolean isSucceed() {
		return succeed;
	}

	@SuppressWarnings("unused")
	private void add(String grounp, String id, String sql) {
		SqlKit.add(grounp, id, sql);
	}

}

@XmlRootElement(name = "jfinal")
class Root {

	@XmlElement(name = "grounp", required = true, type = Grounp.class)
	List<Grounp> grounpList;

}

@XmlRootElement(name = "grounp")
class Grounp {

	@XmlAttribute
	String id;

	@XmlElement(name = "sql", required = false, type = Sql.class)
	List<Sql> sqlList;

}

@XmlRootElement(name = "sql")
class Sql {

	@XmlAttribute
	String id;

	@XmlValue
	String value;
}